import Axios, {
  AxiosInstance,
  AxiosRequestConfig,
  CustomParamsSerializer,
  CancelTokenStatic,
  AxiosResponse,
  AxiosError
} from "axios";
import { RequestMethods } from "./types";
import { stringify } from "qs";
import qs from "qs";
import { ElMessage } from "element-plus";
import { useUserStore } from "@/store/modules/user";
import router from "@/router";
import { BASEURL } from "@/config/urlConfig";

const userStore = useUserStore();

// 相关配置请参考：www.axios-js.com/zh-cn/docs/#axios-request-config-1
const defaultConfig: AxiosRequestConfig = {
  baseURL: BASEURL,
  // 请求超时时间
  timeout: 10000,
  headers: {
    Accept: "application/json, text/plain, */*",
    // 表示请求主体使用JSON格式进行编码
    "Content-Type": "application/json",
    // 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
    "X-Requested-With": "XMLHttpRequest"
  },
  // 数组格式参数序列化（https://github.com/axios/axios/issues/5142）。
  paramsSerializer: {
    serialize: stringify as unknown as CustomParamsSerializer
  }
};

class MyHttp {
  constructor() {
    this.interceptorsRequest();
    this.interceptorsResponse();
  }

  /** 保存当前Axios实例对象 */
  private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);

  /** 存放请求列表 */
  protected pending: Array<{
    url: string;
    cancel: Function;
  }> = [];

  /** 创建 cancel token */
  protected CancelToken: CancelTokenStatic = Axios.CancelToken;

  /** 是否是重复取消请求，而不是路由跳转取消 */
  private static isRemove = false;

  protected interceptorsRequest(): void {
    MyHttp.axiosInstance.interceptors.request.use(
      async (config: any) => {
        this.removePending(config);

        // 将请求添加到列表中
        // config.cancelToken = new this.CancelToken((t: any) => {
        //   this.pending.push({
        //     url: `${config.url}/${JSON.stringify(config.data)}&request_type=${
        //       config.method
        //     }`,
        //     cancel: t
        //   });
        // });

        // this.requestLog(config);
        // 开启进度条动画

        // 请求白名单
        const whiteList = ["/user/login"];
        return whiteList.some(v => config.url.indexOf(v) > -1)
          ? config
          : new Promise(resolve => {
              // 获取token，判断
              const token = userStore.getToken;
              config.headers["Authorization"] = token;
              resolve(config);
            });
      },
      (error: AxiosError) => {
        return Promise.reject(error);
      }
    );
  }

  protected interceptorsResponse(): void {
    MyHttp.axiosInstance.interceptors.response.use(
      (response: AxiosResponse) => {
        console.log(response);
        // 关闭进度条动画

        // this.responseLog(response as AxiosResponse);

        this.removePending(response.config);

        // 状态码判断
        // 状态码判断
        if (response.data.code === 201) {
          // ElMessage.error(response.data.msg);
          // return;
        } else if (response.data.code === 204) {
          ElMessage.error(response.data.msg);
          userStore.LOGOUT();
          router.push("/login");
          return;
        }

        return response.data;
      },
      (error: AxiosError) => {
        // 所有的响应异常 区分来源为取消请求/非取消请求

        // 关闭进度条动画

        if (Axios.isCancel(error)) {
          if (MyHttp.isRemove) {
            // error.message = "请勿重复请求";
            ElMessage({
              message: "请勿重复请求",
              type: "warning",
              showClose: true
            });
          }
        } else {
          // MyHttp.statusHandel(error?.response?.status);
          ElMessage({
            message: "请求超时或服务器异常，请检查网络或联系管理员！",
            type: "error",
            showClose: true
          });
        }

        return Promise.reject(error);
      }
    );
  }

  /** 状态码处理 */
  // protected static statusHandel(status: number): void {
  //   let message: string;
  //   switch (status) {
  //     case 400:
  //       message = "请求错误(400)";
  //       break;
  //     case 401:
  //       message = "未授权，请重新登录(401)";
  //       break;
  //     case 403:
  //       message = "拒绝访问(403)";
  //       break;
  //     case 404:
  //       message = "请求出错(404)";
  //       break;
  //     case 408:
  //       message = "请求超时(408)";
  //       break;
  //     case 500:
  //       message = "服务器错误(500)";
  //       break;
  //     case 501:
  //       message = "服务未实现(501)";
  //       break;
  //     case 502:
  //       message = "网络错误(502)";
  //       break;
  //     case 503:
  //       message = "服务不可用(503)";
  //       break;
  //     case 504:
  //       message = "网络超时(504)";
  //       break;
  //     case 505:
  //       message = "HTTP版本不受支持(505)";
  //       break;
  //     default:
  //       message = `连接出错(${status})!`;
  //   }
  // }

  // 取消重复请求
  protected removePending(config: AxiosRequestConfig): void {
    MyHttp.isRemove = true;

    this.pending.map((v, index) => {
      if (
        v.url ===
        `${config.url}/${JSON.stringify(config.data)}&request_type=${
          config.method
        }`
      ) {
        v.cancel();
        // console.log("=====", this.pending);
        this.pending.splice(index, 1);
        // console.log("+++++", this.pending);
      }
      return v;
    });
  }

  // 请求日志
  protected requestLog(request: AxiosRequestConfig): void {
    if (process.env.NODE_ENV === "development") {
      const randomColor = `rgba(${Math.round(Math.random() * 255)},${Math.round(
        Math.random() * 255
      )},${Math.round(Math.random() * 255)})`;

      console.log(
        "%c┍------------------------------------------------------------------┑",
        `color:${randomColor};`
      );
      console.log("| 请求地址：", request.url);
      console.log(
        "| 请求参数：",
        qs.parse(
          ((request.method || "get").toLowerCase() === "get"
            ? request.params
            : request.data) as any
        )
      );
      console.log(
        "%c┕------------------------------------------------------------------┙",
        `color:${randomColor};`
      );
    }
  }

  // 响应日志
  protected responseLog(response: AxiosResponse): void {
    if (process.env.NODE_ENV === "development") {
      const randomColor = `rgba(${Math.round(Math.random() * 255)},${Math.round(
        Math.random() * 255
      )},${Math.round(Math.random() * 255)})`;

      console.log(
        "%c┍------------------------------------------------------------------┑",
        `color:${randomColor};`
      );
      console.log("| 请求地址：", response.config.url);
      // console.log("| 请求参数：", qs.parse(response.config.data as any));
      console.log("| 返回数据：", response.data);
      console.log(
        "%c┕------------------------------------------------------------------┙",
        `color:${randomColor};`
      );
    }
  }

  /** 通用请求工具函数 */
  public request<T>(
    method: RequestMethods,
    url: string,
    param?: AxiosRequestConfig,
    axiosConfig?: AxiosRequestConfig
  ): Promise<T> {
    const config = {
      method,
      url,
      ...param,
      ...axiosConfig
    } as AxiosRequestConfig;

    // 单独处理自定义请求/响应回掉
    return new Promise((resolve, reject) => {
      MyHttp.axiosInstance
        .request(config)
        .then((response: any) => {
          resolve(response);
        })
        .catch(error => {
          reject(error);
        });
    });
  }

  /** 单独抽离的 post 工具函数 */
  public post<T, P>(
    url: string,
    params?: AxiosRequestConfig<T>,
    config?: AxiosRequestConfig
  ): Promise<P> {
    return this.request<P>("post", url, params, config);
  }

  /** 单独抽离的 delete 工具函数 */
  public delete<T, P>(
    url: string,
    params?: AxiosRequestConfig<T>,
    config?: AxiosRequestConfig
  ): Promise<P> {
    return this.request<P>("delete", url, params, config);
  }

  /** 单独抽离的 put 工具函数 */
  public put<T, P>(
    url: string,
    params?: AxiosRequestConfig<T>,
    config?: AxiosRequestConfig
  ): Promise<P> {
    return this.request<P>("put", url, params, config);
  }

  /** 单独抽离的get工具函数 */
  public get<T, P>(
    url: string,
    params?: AxiosRequestConfig<T>,
    config?: AxiosRequestConfig
  ): Promise<P> {
    return this.request<P>("get", url, params, config);
  }
}

export const http = new MyHttp();
